﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestByWire.Tutorial
{
	[TestClass]
	public class Properties : TestTarget<Person>
	{
		[TestMethod]
		public void StubbingPropertiesRequiresReturnTypeSpecification()
		{
			Given.That<IName, string>(n => n.FirstName).Returns("Peter");

			var firstName = When.Calling.FirstName;

			Assert.AreEqual("Peter", firstName);
		}

		[TestMethod]
		public void StubbingInterfacePropertyWithConcreteTypeMustBePossible()
		{
			Given.That<IName, IList<DateTime>>(n => n.ModifiedDates).Returns(new List<DateTime>());

			int modifiedCount = When.Calling.ModifiedCount();

			Assert.AreEqual(0, modifiedCount);
		}
	}

	[TestClass]
	public class PropertiesOnTarget : TestTarget<Person>
	{
		[TestMethod]
		public void AssertingPropertyChangedOnTarget()
		{
			Given.That<IName, string>(n => n.FirstName).Returns("John");

			When.Calling.ChangeName(With<IName>.Dependency);

			Then.TargetFulfills(t => t.FirstName == "John");
		}
	}

	[TestClass]
	public class LateBoundProperties : TestTarget<PersonRequiringLateBoundStubbing>
	{
		[TestMethod]
		[UseStubLateBound(typeof(IName), "_name")]
		public void StubbingPropertiesRequiresReturnTypeSpecification()
		{
			Given.That<IName, string>("_name", n => n.FirstName).Returns("Peter");

			var firstName = When.Calling.FirstName;

			Assert.AreEqual("Peter", firstName);
		}
	}

	[TestClass]
	public class PropertiesInScenarios : TestTarget<Person>
	{
		[TestMethod]
		public void StubbingPropertiesInScenariosRequiresReturnTypeSpecification()
		{
			Scenario.Is(FirstNameIsPeter);

			var firstName = When.Calling.FirstName;

			Assert.AreEqual("Peter", firstName);
		}

		[TestMethod]
		public void StubbingInterfacePropertyInScenarioWithConcreteTypeMustBePossible()
		{
			Scenario.Is(ModifiedDatesAreEmpty);

			int modifiedCount = When.Calling.ModifiedCount();

			Assert.AreEqual(0, modifiedCount);
		}

		private static void FirstNameIsPeter()
		{
			A.CallTo<IName, string>(n => n.FirstName).Returns("Peter");
		}

		private static void ModifiedDatesAreEmpty()
		{
			A.CallTo<IName, IList<DateTime>>(n => n.ModifiedDates).Returns(new List<DateTime>());
		}
	}

	public class Person
	{
		private IName _name;

		public Person(IName name)
		{
			_name = name;
		}

		public void ChangeName(IName name)
		{
			_name = name;
		}

		public string FirstName
		{
			get
			{
				return _name.FirstName;
			}
		}

		public int ModifiedCount()
		{
			return _name.ModifiedDates.Count;
		}
	}

	public class PersonRequiringLateBoundStubbing
	{
		private readonly IName _name;

		public PersonRequiringLateBoundStubbing()
		{
			_name = new Name();
		}

		public string FirstName
		{
			get
			{
				return _name.FirstName;
			}
		}

		public int ModifiedCount()
		{
			return _name.ModifiedDates.Count;
		}
	}

	public interface IName
	{
		string FirstName { get; }

		IList<DateTime> ModifiedDates { get; }
	}

	internal class Name : IName
	{
		public string FirstName
		{
			get
			{
				return string.Empty;
			}
		}

		public IList<DateTime> ModifiedDates
		{
			get
			{
				return new List<DateTime>();
			}
		}
	}
}
